﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CommonLib {
    public class AABB : Primitive {
        public const int GRID_SIZE = 4;

        public Cell box;
        public Vector3c[] lightGrid;
        public Vector3c lightTile;

        public AABB(Cell _box, bool _light) {
            this.box = _box;
            this.light = _light;

            this.lightTile = _box.vmax;
            this.lightTile.sub(_box.vmin);
            this.lightTile.x /= GRID_SIZE;
            this.lightTile.y = 0;
            this.lightTile.z /= GRID_SIZE;

            this.lightGrid = new Vector3c[GRID_SIZE * GRID_SIZE];
            for (int z = 0; z < GRID_SIZE; z++) {
                for (int x = 0; x < GRID_SIZE; x++) {
                    int idx = x + z * GRID_SIZE;
                    this.lightGrid[idx] = _box.vmin;
                    this.lightGrid[idx].x += this.lightTile.x * x;
                    this.lightGrid[idx].z += this.lightTile.z * z;
                }
            }
        }

        public override Vector3c getColor(Vector3c pos) {
            return material.color;
        }
        public override Cell getBoundingBox() {
            return box;
        }
        public override Vector3c getNormal(Vector3c boundryPoint) {
            var VMin = box.vmin;
            var VMax = box.vmax;
            double[] d = new double[6];
            d[0] = Math.Abs(VMin.x - boundryPoint.x);
            d[1] = Math.Abs(VMax.x - boundryPoint.x);
            d[2] = Math.Abs(VMin.y - boundryPoint.y);
            d[3] = Math.Abs(VMax.y - boundryPoint.y);
            d[4] = Math.Abs(VMin.z - boundryPoint.z);
            d[5] = Math.Abs(VMax.z - boundryPoint.z);

            int face = 0;
            double dist = d[0];
            for (int i = 1; i < d.Length; i++) {
                if (d[i] < dist) {
                    face = i;
                    dist = d[i];
                }
            }

            switch (face) {
                case 0:
                    return new Vector3c(-1, 0, 0);
                case 1:
                    return new Vector3c(1, 0, 0);
                case 2:
                    return new Vector3c(0, -1, 0);
                case 3:
                    return new Vector3c(0, 1, 0);
                case 4:
                    return new Vector3c(0, 0, -1);
                default:
                    return new Vector3c(0, 0, 1);
            }
        }
        public override Intersection isIntersect(Cell b) {
            var c0 = box.center;
            var c1 = b.center;

            Vector3c link = c0;
            link.sub(c1);

            if (Math.Abs(link.x) <= (c0.x + c1.x)
                && Math.Abs(link.y) <= (c0.y + c1.y)
                && Math.Abs(link.z) <= (c0.z + c1.z))
                return Intersection.Intersecting;

            return Intersection.Outside;
        }
        public override Orientation isIntersect(Ray r, ref double dist) {
            double tnear = Double.MinValue;
            double tfar = Double.MaxValue;

            if (r.direction.x == 0) {
                if (r.position.x < box.vmin.x || box.vmax.x < r.position.x)
                    return Orientation.Nonexistent;
            } else {
                double t1 = (box.vmin.x - r.position.x) / r.direction.x;
                double t2 = (box.vmax.x - r.position.x) / r.direction.x;
                if (t1 > t2) {
                    double tmp = t1;
                    t1 = t2;
                    t2 = tmp;
                }
                if (t1 > tnear)
                    tnear = t1;
                if (tfar > t2)
                    tfar = t2;
                if (tnear > tfar || tfar < 0)
                    return Orientation.Nonexistent;
            }
            if (r.direction.y == 0) {
                if (r.position.y < box.vmin.y || box.vmax.y < r.position.y)
                    return Orientation.Nonexistent;
            } else {
                double t1 = (box.vmin.y - r.position.y) / r.direction.y;
                double t2 = (box.vmax.y - r.position.y) / r.direction.y;
                if (t1 > t2) {
                    double tmp = t1;
                    t1 = t2;
                    t2 = tmp;
                }
                if (t1 > tnear)
                    tnear = t1;
                if (tfar > t2)
                    tfar = t2;
                if (tnear > tfar || tfar < 0)
                    return Orientation.Nonexistent;
            }
            if (r.direction.z == 0) {
                if (r.position.z < box.vmin.z || box.vmax.z < r.position.z)
                    return Orientation.Nonexistent;
            } else {
                double t1 = (box.vmin.z - r.position.z) / r.direction.z;
                double t2 = (box.vmax.z - r.position.z) / r.direction.z;
                if (t1 > t2) {
                    double tmp = t1;
                    t1 = t2;
                    t2 = tmp;
                }
                if (t1 > tnear)
                    tnear = t1;
                if (tfar > t2)
                    tfar = t2;
                if (tnear > tfar || tfar < 0)
                    return Orientation.Nonexistent;
            }

            if (0 < tnear) {
                if (tnear < dist) {
                    dist = tnear;
                    return Orientation.Exterior;
                }
                return Orientation.Nonexistent;
            }
            if (tfar < dist) {
                dist = tfar;
                return Orientation.Interior;
            }
            return Orientation.Nonexistent;
        }
        public override Vector3c getCenter() {
            return box.center;
        }
    }
}
